/// <reference path = "./ShaderComponent.ts" />

module lcc$render {

const {ccclass, property, menu } = cc._decorator;

//@ts-ignore
let gfx = cc.gfx;

/**
 * 值配置
 */
const VC ={
	tag : "vec4",

	varSelect : true,
	varDeftype : VariableType.UNIFORM,
	varAttrMacro : "ATTR_VEC4",
	varAttrMacroCheckOnly : false,
	varAttrName : "a_vec4v",
	varUnifMacro : "UNIF_VEC4",
	varUnifMacroCheckOnly : false,
	varUnifName : "u_vec4v",
	
	valDefault : new cc.Vec4(),
	valNew : ()=>{ return new cc.Vec4(); },
	valType : cc.Vec4,

	typeSize : 4,

	toArray : (arr:number[], value:cc.Vec4, offest:number) => {
		arr[offest] = value.x;
		arr[offest + 1] = value.y;
		arr[offest + 2] = value.z;
		arr[offest + 3] = value.w;
	}
}

@ccclass("render.ShaderVec4")
@menu("i18n:lcc-render.menu_component/ShaderVec4")
export class ShaderVec4 extends ShaderComponent {

	_tag:string = VC.tag;

	@property({
		type :cc.Enum(ValueType)
	})
	_valueType:ValueType = ValueType.SINGLE;
	@property({
		type :cc.Enum(ValueType),
		tooltip : "值类型"
	})
	get valueType(){
		return this._valueType;
	}
	set valueType(value:ValueType){
		if(this._valueType != value){
			this._valueType = value;
			if(value == ValueType.ARRAY){
                this._valueVar.type == VariableType.UNIFORM;
                this._valueVar._typesel = false;
            }else{
                this._valueVar._typesel = true;
            }
		}
	}

	@property(VariableConfig)
	_valueVar:VariableConfig = new VariableConfig(
		VC.varSelect, VC.varDeftype, 
		VC.varAttrMacro, VC.varAttrMacroCheckOnly, VC.varAttrName, 
		VC.varUnifMacro, VC.varUnifMacroCheckOnly, VC.varUnifName);
	@property({
		type : VariableConfig,
		tooltip : "值变量"
	})
	get valueVar(){
		return this._valueVar;
	}
	set valueVar(value:VariableConfig){
		this._valueVar = value;
		this.onUpdateValues();
		this.onRenderUpdateMaterial();
		this.node.emit("shader_update_attribute");
    }
    
    @property([VC.valType])
	_values:typeof VC.valDefault[] = [ VC.valNew() ];
	
	@property({
		visible (){
			return this._valueType == ValueType.SINGLE && this._valueVar.type == VariableType.UNIFORM;
		},
		type : VC.valType,
		tooltip : "值"
	})
	get value(){
		return this._values[0];
	}
	set value(value_:typeof VC.valDefault){
		this._values[0] = value_;
		this.onUpdateValues();
	}

	@property({
		visible (){
			return this._valueType == ValueType.ARRAY || this._valueVar.type == VariableType.ATTRIBUTE;
		},
		type : [VC.valType],
		tooltip : "值数组/值顶点数组"
	})
	get values(){
		return this._values;
	}
	set values(value:typeof VC.valDefault[]){
		this._values = value;
		this.onUpdateValues();
	}

	/**
	 * 值偏移
	 */
    private _valueOffset:number = 0;

    onLoad(){
        this.node.on("render_shape_checked", this.onRenderShapeChecked, this);
        this.node.on("render_update_material", this.onRenderUpdateMaterial, this);
		this.node.on("render_update_attribute", this.onRenderUpdateAttribute, this);
		this.node.on("render_update_vertex", this.onRenderUpdateRenderData, this);
    }

    onDestroy(){
		this.node.targetOff(this);
    }
    
	onEnable(){
        this.onStateUpdate(true);
	}

	onDisable(){
        this.onStateUpdate(false);
    }

    private onStateUpdate(enable:boolean){
		this.node.emit("shader_update_tag");
		this.onUpdateValues();
	}
	
    /**
     * 检查形状
     */
    private onRenderShapeChecked(){
        if(this._valueVar.type == VariableType.ATTRIBUTE){
            this.onUpdateValues();
        }
    }
	
	/**
	 * 检查值
	 */
	private onUpdateValues(){
		if(this.enabled){
            if(this._values.length <= 0){
                this._values = [ VC.valNew() ];
            }
            if(this._valueVar.type == VariableType.ATTRIBUTE){
                let rsys = this.getComponent(RenderSystem);
                if(rsys){
                    let vc = rsys._verticesCount;
                    if(this._values.length < vc){
                        let values = this._values;
                        for(let i = 0; i < vc; i++){
                            let color = values[i];
                            if(color == null){
                                values[i] = VC.valNew();
                            }
                        }
                    }
                    this._values.length = vc;
                }
            }else if(this._valueType == ValueType.SINGLE){
                this._values.length = 1;
            }
            if(this._valueVar.type == VariableType.ATTRIBUTE){
                this.node.emit("shader_update_attribute");
            }else{
                this.onRenderUpdateMaterial();
            }
		}
	}
    
    private onRenderUpdateMaterial (comp:RenderSystem = this.getComponent(RenderSystem)) {
		if(comp){
			// make sure material is belong to self.
			// @ts-ignore
			let material = comp._materials[0];
			if (material) {
				if (this.checkMaterialMacro(material, this._valueVar.unifMacro)) {
					if(this.enabled && this._valueVar.type === VariableType.UNIFORM){
                        this.defineMaterialMacro(material, this._valueVar.unifMacro, true);
                        if(this._valueType == ValueType.SINGLE){
                            material.setProperty(this._valueVar.unifName, this._values[0]);
                        }else{
                            let values = this._values;
                            let _values:number[] = [];
                            let defv = VC.valDefault;
                            for(let i = 0, l = values.length; i < l; i++){
                                VC.toArray(_values, values[i] || defv, _values.length);
                            }
                            material.setProperty(this._valueVar.unifName, new Float32Array(values.length * VC.typeSize));
                            material.setProperty(this._valueVar.unifName, _values);
                        }
						//Editor.log("onRenderUpdateMaterial", this._value);
					}else{
						this.defineMaterialMacro(material, this._valueVar.unifMacro, false);
					}
					//Editor.log("onRenderUpdateMaterial", this.enabled);
				}
			}
		}
    }
	
	/**
	 * 获得属性值
	 * @param comp 
	 */
	private getAttributeValues(comp:RenderSystem){
		let values:number[] = [];
		let vc = comp._verticesCount;
        let defv = VC.valDefault;
        for(let i = 0; i < vc; i++){
            VC.toArray(values, this._values[i] || defv, values.length);
        }
		return values;
	}

	private onRenderUpdateAttribute(comp:RenderSystem = this.getComponent(RenderSystem)){
		if(comp){
			// @ts-ignore
			let material = comp._materials[0];
			if (material) {
				if (this.checkMaterialMacro(material, this._valueVar.attrMacro)) {
					if(this.enabled && this._valueVar.type === VariableType.ATTRIBUTE){
						this.defineMaterialMacro(material, this._valueVar.attrMacro, true);
						this._valueOffset = comp.addVertexAttribute({ 
							name: this._valueVar.attrName, 
							type: gfx.ATTR_TYPE_FLOAT32, 
							num: VC.typeSize, 
						}, VC.typeSize);
						//Editor.log("onRenderUpdateAttribute", this._valueOffset);
					}else{
						this.defineMaterialMacro(material, this._valueVar.attrMacro, false);
						this._valueOffset = 0;
					}
					//Editor.log("onRenderUpdateAttribute", this.enabled);
				}
			}
		}
	}

	private onRenderUpdateRenderData(comp:RenderSystem = this.getComponent(RenderSystem)){
        if(comp && this.enabled){
			//Editor.log("onRenderUpdateRenderData");
			if(this._valueOffset > 0){
				// @ts-ignore
				let assembler = comp._assembler;
				let values = this.getAttributeValues(comp);
				let vlen = values.length;
				let valueOffset = this._valueOffset;
				let floatsPerVert = assembler.floatsPerVert;
				let verts = assembler.renderData.vDatas[0];
				for (let i = 0; i < 4; i++) {
					let dstOffset = floatsPerVert * i + valueOffset;
					let srcOffset = VC.typeSize * i;
					if(srcOffset >= vlen){
						srcOffset = 0;
					}
					for(let j = 0; j < VC.typeSize; j++){
						verts[dstOffset + j] = values[srcOffset + j];
					}
				}
			}
        }
	}
}

}
